# -*- coding: utf-8 -*-
# vim: set noet ts=4:
#
# scim-python
#
# Copyright (c) 2007-2008 Yu Fan <yufanyufan@gmail.com>
#
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
# Boston, MA  02111-1307  USA
#
# $Id: $
#
import scim
import os
from scim import KeyCode
from scim import KeyMask
from scim import Property
import traceback
import sys
from PYDict import *
from gettext import dgettext
from ShuangPinDB import *
import curses.ascii as ascii

_ = lambda a : dgettext ("scim-python", a)

IMEngine = scim.IMEngine
IMEngineFactory = scim.IMEngineFactory

(YLEN, Y0, Y1, Y2, Y3, YX, PHRASE, ADJ_FREQ) = range (0, 8)

class PinYinWord:
	def __init__ (self, shengmu, yunmu):
		self.shengmu = shengmu
		self.yunmu = yunmu
		self.char = ""
		self._pinyin_id = None
		if yunmu != "":
			self._pinyin_id = PINYIN_DICT [self.get_pinyin()]
			self._sheng_mu_id = SHENGMU_DICT [self.get_shengmu()]
		else:
			self._sheng_mu_id = SHENGMU_DICT [self.get_shengmu()]
	def get_sheng_mu_id (self):
		return self._sheng_mu_id
	def get_pinyin_id (self):
		return self._pinyin_id
	def set_pinyin_id (self, id):
		self._pinyin_id = id
	def get_shengmu(self):
		if self.shengmu == "'":
			return ""
		return self.shengmu
	def set_shengmu(self,shengmu):
		self.shengmu = shengmu	
	def set_yunmu(self,yunmu):
		self.yunmu = yunmu
		if(yunmu != ""):
			self._pinyin_id = PINYIN_DICT [ self.get_pinyin() ]
		else:
			self._pinyin_id = None
	def set_char (self,char):
		self.char = char
	def get_pinyin(self):
		if self.shengmu == "'":
			return self.yunmu
		return self.shengmu + self.yunmu
	def get_screen_pinyin(self):
		if self.shengmu == "'":
			return "'"+self.yunmu
		return self.shengmu + self.yunmu		
	def __str__ (self):
		return self.get_pinyin()
	def is_complete (self):
		return self._pinyin_id != None
	def is_char (self):
		return self.char != ""
		
class Editor:
	database = ShuangPinDB()
	def __init__ (self):
		self.clear()
	def clear(self):
		self.cursor = 0
		self.wordlist = []
		self.pinyinlist = []
		self.candidates = []
		self.predict = []
		self.page = 0
		Editor.database.clear_cache()
	def current (self):
		if self.pinyinlist:
			return self.pinyinlist[-1]
		elif self.wordlist and self.cursor>0:
			return self.wordlist[self.cursor-1]
		else:
			return None
	def is_empty (self):
		return (not self.pinyinlist) and (not self.wordlist)
	def is_end (self):
		return self.is_empty() or (not self.pinyinlist) and self.cursor == len (self.wordlist)
	def insert(self,pinyin):
		self.pinyinlist.append (pinyin)
		self.update()
		self.auto_convert()
	def get_aux (self):
		return "".join ( u[PHRASE] for u in self.predict)[:len(self.pinyinlist) ]
	def get_preedit (self):
		return u"".join( [i.char for i in self.wordlist[0:self.cursor] ] ) +\
			u"".join ([i.get_screen_pinyin() for i in self.pinyinlist])+ \
			u"".join ( [i.char for i in self.wordlist[self.cursor:]] )
	def get_screen_cursor (self):
		return self.cursor+ len(u"".join([i.get_screen_pinyin() for i in self.pinyinlist]))
	def pinyin_auto_select (self, phrase):
		length = len (phrase)
		for i in range(0, length):
			self.pinyinlist[i].set_char(phrase[i])
		self.wordlist[self.cursor:self.cursor] = self.pinyinlist[:length]
		del self.pinyinlist[:length]
		self.cursor += length
		self.update()		
	def pinyin_manual_select (self,candidate):
		#~ print phrase
		phrase = candidate[PHRASE]
		length = len(phrase)
		#~ length = len(phrase) if len(phrase)<len(self.pinyinlist) else len(self.pinyinlist)
		for i in range(0,length):
			self.pinyinlist[i].set_char(phrase[i])
			if not self.pinyinlist[i].is_complete():
				if i<4:
					self.pinyinlist[i].set_pinyin_id (candidate[i+1])
				else:
					print candidate[YX]
					py = candidate[YX].split("'")
					print py[i-5]
					self.pinyinlist[i].set_pinyin_id (PINYIN_DICT[py[i-4]])
					print self.pinyinlist[i]._pinyin_id
			self.pinyinlist[i].manual = True
		self.wordlist[self.cursor:self.cursor] = self.pinyinlist[:length]
		del self.pinyinlist[:length]
		self.cursor += length
		self.update()
	def convert_all (self):
		while self.pinyinlist:
			self.pinyin_manual_select (self.predict[0])
	def jump_to_next_word(self):
		predict = self.get_predict (self.wordlist[self.cursor:])
		self.cursor += predict[0][YLEN]
		self.update ()
	def auto_convert (self):
		if len (self.get_aux()) < len (self.pinyinlist):
			if self.predict:
				self.pinyin_auto_select (self.predict[0][PHRASE])
	def update (self):
		self.update_predict()
		self.update_candates()
	def update_predict (self):
		if self.pinyinlist:
			self.predict = self.get_predict_pinyinlist (self.pinyinlist)
		else:
			self.predict = []
	def split_phrase (self, string, sentence):
		start = 0
		phrase_list = []
		while start < len(self.wordlist)-1:
			phrase = Editor.database.select_words_by_pinyin_list_all (self.wordlist[start:start+2])
			candidate = None
			for i in phrase:
				if i[PHRASE] == string[start:start + len(i[PHRASE]) ]:
					if not candidate or candidate[PHRASE] < i[PHRASE]:
						candidate = i
			if candidate == None:
				phrase_list.append ( (start, 1, string[start]))
				start += 1
			else:
				phrase_list.append ( (start, len(candidate[PHRASE]), candidate[PHRASE]))
				start += len(candidate[PHRASE])
		if start < len (self.wordlist):
			phrase_list.append ((start,1,string[-1]))
		start = 0
		while start < len (phrase_list):
			tmp_phrase_start = phrase_list[start][0]
			tmp_phrase = ""
			while start < len (phrase_list) and phrase_list[start][1] == 1 \
				and string[phrase_list[start][0]] != sentence[phrase_list[start][0]]:
				tmp_phrase += phrase_list[start][2]
				del phrase_list[start]			
			if tmp_phrase:
				phrase_list.insert (start, (tmp_phrase_start, len(tmp_phrase), tmp_phrase) )
			if len (tmp_phrase) > 1:
				Editor.database.add_phrase (self.wordlist[tmp_phrase_start:tmp_phrase_start + len(tmp_phrase)]) 	
			start+=1
		return phrase_list
	def split_predict (self):
		predict = []
		start = 0
		while start < len (self.wordlist):
			p = self.get_predict (self.wordlist[start:])
			predict.append ( (start,len(p[0][PHRASE]), p[0][PHRASE]) )
			start += len (p[0][PHRASE])
		return predict
	def addphrase (self, phrase_list, pstart, pend):
		if pstart < 0:
			return
		if pend >= len (phrase_list):
			return
		Editor.database.add_phrase(\
			self.wordlist[phrase_list[pstart][0]:(phrase_list[pend][0]+phrase_list[pend][1])])
	def adjust_freq (self, phrase_list):
		p = [ self.wordlist[i[0]:i[0]+i[1]] for i in phrase_list]
		for i in p:
			Editor.database.adjust_phrase_freq (i)
	def learn (self):
		predict = self.split_predict ()
		sentence = u"".join ([ i[2] for i in predict])
		string = self.get_preedit ()
		phrase_list = self.split_phrase (string, sentence)
		#~ print "out"
		#~ for i in phrase_list:
			#~ print i[1],i[2]

		#~ for i in predict:
			#~ print i[1],i[2]
		cur_phrase = 0
		cur_predict = 0
		phrase_begin = 0
		predict_begin = 0
		while cur_phrase < len(phrase_list):
			while predict[cur_predict][0]+ predict[cur_predict][1] < phrase_list[cur_phrase][0] + phrase_list[cur_phrase][1]:
				cur_predict += 1
			if predict[cur_predict][0]+ predict[cur_predict][1] > phrase_list[cur_phrase][0] + phrase_list[cur_phrase][1]:
				cur_phrase += 1
			else:
				#~ print string[phrase_list[phrase_begin][0]:phrase_list[cur_phrase][0]+phrase_list[cur_phrase][1]]
				#~ print sentence[predict[predict_begin][0]:predict[cur_predict][0]+predict[cur_predict][1]]
				if string[phrase_list[phrase_begin][0]:phrase_list[cur_phrase][0] + phrase_list[cur_phrase][1]]!=\
					sentence[predict[predict_begin][0]:predict[cur_predict][0] + predict[cur_predict][1]]:
					if cur_phrase-phrase_begin==0:
						if cur_predict-predict_begin ==0:
							self.addphrase(phrase_list, phrase_begin-1, cur_phrase)
							self.addphrase(phrase_list, phrase_begin, cur_phrase+1)
					else:
						self.addphrase (phrase_list, phrase_begin, cur_phrase)
				phrase_begin = cur_phrase + 1
				predict_begin = cur_predict + 1
				cur_phrase += 1
		self.adjust_freq (phrase_list)

	def get_predict_pinyinlist (self, pinyinlist):
		longest = Editor.database.get_longest_phrase_length (pinyinlist)
		if longest >= len(pinyinlist):
			candidates = Editor.database.select_words_by_pinyin_list (pinyinlist)
			if candidates and len (candidates[0][PHRASE]) == len (pinyinlist):
				#~ print "phrase1",candidates[0][PHRASE],candidates[0][ADJ_FREQ]
				return [candidates[0]]
			else:
				candidates = Editor.database.select_words_by_pinyin_list_all(pinyinlist)
				if candidates:
					p = list (candidates[0]);
					p[YLEN] = len (pinyinlist)
					p[PHRASE] = p[PHRASE][:p[YLEN]]
					return [p]
		max_freq = 0
		predict = []
		length = len (pinyinlist)
		for i in range (1, length):
			candidates = Editor.database.select_words_by_pinyin_list (pinyinlist[:i])
			if not candidates:
				continue
			candidates2 = Editor.database.select_words_by_pinyin_list(pinyinlist[i:length])
			if not candidates2:
				continue
			tmp_phrase = candidates[0]
			tmp_phrase2 = candidates2[0]
			if tmp_phrase[ADJ_FREQ] + tmp_phrase2[ADJ_FREQ] >= max_freq:
				predict = [tmp_phrase, tmp_phrase2]
				#~ print tmp_phrase[PHRASE],tmp_phrase2[PHRASE], tmp_phrase[ADJ_FREQ],tmp_phrase2[ADJ_FREQ]
				max_freq = tmp_phrase[ADJ_FREQ] + tmp_phrase2[ADJ_FREQ]
		if predict:
			return predict
		else:
			return self.predict

	def get_predict (self, pinyinlist):
		if not pinyinlist:
			return []
		longest = Editor.database.get_longest_phrase_length (pinyinlist)
		if longest >= len (pinyinlist):
			candidates = Editor.database.select_words_by_pinyin_list(pinyinlist)
			if candidates and len (candidates[0][PHRASE]) == len (pinyinlist):
				#~ print "phrase1",candidates[0][PHRASE],candidates[0][ADJ_FREQ]
				return [candidates[0]]
			else:
				candidates = Editor.database.select_words_by_pinyin_list_all(pinyinlist)
				if candidates:
					p = list (candidates[0]);
					p[YLEN] = len (pinyinlist)
					p[PHRASE] = p[PHRASE][:p[YLEN]]
					return [p]
		max_freq = 0
		max_length =0
		#~ print "try words"
		#~ if longest==1:
			#~ return [Editor.database.select_words_by_pinyin_list(pinyinlist[:1])[0][PHRASE]]
		#~ print longest
		for i in range (1, longest + 1):
			candidates = Editor.database.select_words_by_pinyin_list (pinyinlist[:i])
			if not candidates:
				continue
			tmp_phrase = candidates[0]
			tmp_freq = tmp_phrase[ADJ_FREQ]
			longest2 = Editor.database.get_longest_phrase_length (pinyinlist[i:])
			#~ print "phrase1",tmp_phrase[PHRASE]
			#~ print i,longest2
			for p in range(i + longest2,i-1,-1):
				if p < max_length:
					continue
				candidates2 = Editor.database.select_words_by_pinyin_list(pinyinlist[i:p+1])
				#~ print len(candidates2)
				if candidates2:
					tmp_phrase2 = candidates2[0]
					#~ print "phrase2",tmp_phrase2[PHRASE]
					tmp_freq2 = tmp_phrase2[ADJ_FREQ]
					#~ print tmp_phrase, " ", candidates2[0][PYSQLiteDB.PHRASE]
					if p > max_length or \
						(tmp_phrase[ADJ_FREQ] + tmp_phrase2[ADJ_FREQ] >= max_freq and p == max_length):
						predict = [tmp_phrase, tmp_phrase2]
						#~ print tmp_phrase[PHRASE],tmp_phrase2[PHRASE], tmp_phrase[ADJ_FREQ],tmp_phrase2[ADJ_FREQ]
						max_freq = tmp_freq+tmp_freq2
						max_length = p
		#~ print "get_predict" + predict[0], max_length
		return predict

	def reparse (self, start):
		if start == len (self.wordlist):
			return
		predict = self.get_predict (self.wordlist[start:])
		phrase = predict[0][PHRASE]
		length = len (phrase) 
		#~ if len(phrase)<len(self.wordlist)-start \
					#~ else len(self.wordlist)-start
		#~ print string
		for i in range(0,length):
			self.wordlist[start+i].set_char(phrase[i])
		self.reparse (start+length)

	def select (self, n):
		assert self.page+n < len(self.candidates)
		candidate = self.candidates[self.page+n]
		if self.pinyinlist:
			self.pinyin_manual_select(candidate)
		else:
			phrase = candidate[PHRASE]
			for i in range (0,len (phrase) ):
				self.wordlist[self.cursor+i].set_char(phrase[i])
			self.cursor += len (phrase)
			if self.cursor < len (self.wordlist):
				self.reparse (self.cursor);
		self.update()
	def commit (self):
		if self.pinyinlist:
			self.convert_all ()
		string = self.get_preedit ()
		self.learn ()
		self.clear ()
		return string
	def del_current (self):
		if self.pinyinlist:
			if self.pinyinlist[-1].is_complete ():
				self.pinyinlist[-1].set_yunmu ("")
			else:
				del self.pinyinlist[-1]
		elif self.cursor > 0:
			del self.wordlist[self.cursor-1]
			self.cursor-=1
			if len (self.wordlist) == 0:
				self.clear ()
		elif self.wordlist and self.cursor == 0:
			raise Exception()
		self.update()
	def del_next (self):
		if self.pinyinlist or self.cursor == len (self.wordlist):
			raise Exception ()
		else:
			del self.wordlist[self.cursor]
			if len (self.wordlist)==0:
				self.clear ()
	def move_cursor (self, move):
		if self.is_empty() or self.pinyinlist:
			raise Exception()
		self.cursor += move
		if self.cursor < 0:
			self.cursor += len (self.wordlist)
		elif self.cursor > len (self.wordlist):
			self.cursor = 0
		self.update ()
	def move_cursor_to (self, pos):
		if self.is_empty ():
			raise Exception ()
		if self.pinyinlist:
			self.convert_all ()
		if pos == 0:
			self.cursor = len(self.wordlist)
		elif pos > len(self.wordlist) + 1:
			raise Exception ()
		else:
			self.cursor = pos - 1
		self.update ()
	def update_candates (self):
		if self.is_empty():
			self.candidates = []
		elif self.pinyinlist:
			self.candidates = []
			for i in range (len (self.pinyinlist), 0, -1):
				self.candidates += Editor.database.select_words_by_pinyin_list (self.pinyinlist[:i])
		elif len(self.wordlist)>self.cursor:
			self.candidates = []
			for i in range (len (self.wordlist),self.cursor, -1):
				self.candidates += Editor.database.select_words_by_pinyin_list (self.wordlist[self.cursor:i])
		else:
			self.candidates = []
		self.page = 0
	def get_lookup_table (self):
		if len (self.candidates)-self.page>9:
			return [c[PHRASE] for c in self.candidates[self.page:self.page+10]]
		else:
			return [c[PHRASE] for c in self.candidates[self.page:]]
	def turn_page (self, num):
		if num>0:
			if	num+self.page < len (self.candidates):
				self.page += num
			else:
				self.page =0
		else:
			if self.page >= -num:
				self.page += num

class Engine (IMEngine):
	def __init__ (self, factory, config, encoding, id):
		IMEngine.__init__ (self, factory, config, encoding, id)
		self._editor = Editor ()
		self._lookup_table = scim.LookupTable (9)
		self._status_property = Property ("chinese", "CN")
		
	def reset(self):
		self._editor.clear()
		self._double_quotation_state = False
		self._single_quotation_state = False
		self._prev_key = None
		self._chinese_mode = True
		self.update ()	
		props = [self._status_property]
		self.register_properties (props)
		self.update_properties ()

	def update_preedit (self):
		string = self._editor.get_preedit ()
		if (string == u""):
			self.hide_preedit_string ()
		else:
			self.show_preedit_string ()
			attrs = []
			self.update_preedit_string (string, attrs)
			self.update_preedit_caret (self._editor.get_screen_cursor())

	def focus_out(self):
		self.reset()
		IMEngine.focus_out (self)
		
	def focus_in (self):
		self.reset()
		IMEngine.focus_in (self)
	
	def trigger_property (self, property):
		if property == "chinese":
			self.change_mode ()

	def update_candidate (self):
		candidates = self._editor.get_lookup_table()
		if len (candidates) == 0:
			self.hide_lookup_table ()
		else:
			#~ self.hide_lookup_table ()
			self._lookup_table.clear ()
			for c in candidates:
				self._lookup_table.append_candidate (c)
			self.update_lookup_table (self._lookup_table)
			self.show_lookup_table()

	def update_aux(self):
		if self._editor.predict:
			self.show_aux_string ()
			self.update_aux_string (self._editor.get_aux ())
		else:
			self.hide_aux_string ()
			self.update_aux_string (u"")

	def update (self):
		self.update_preedit ()
		self.update_aux ()
		self.update_candidate ()

	def update_properties (self):
		if self._chinese_mode: # refresh mode
			self._status_property.label = _("CN")
		else:
			self._status_property.label = _("EN")
		self.update_property(self._status_property)

	def change_mode(self):
		if self._chinese_mode:
			self.commit_string (self._editor.commit())
			self.update()
		self._chinese_mode = not self._chinese_mode
		self.update_properties ()
		#~ print "change_mode", self._chinese_mode
	
	def process_key_event (self, key):
		if self._chinese_mode:
			if (key.code == KeyCode.KEY_Shift_L or key.code == KeyCode.KEY_Shift_R)\
			and key.mask == KeyMask.ShiftMask + KeyMask.ReleaseMask:
				if self._prev_key.code == KeyCode.KEY_Shift_L or self._prev_key.code == KeyCode.KEY_Shift_R:
					self.change_mode()		
			elif key.mask == KeyMask.ShiftMask and (key.code != KeyCode.KEY_Shift_L or key.code != KeyCode.KEY_Shift_R)\
				or key.mask & KeyMask.CapsLockMask:
				self.change_mode()			
		else:
			if (key.code == KeyCode.KEY_Shift_L or key.code == KeyCode.KEY_Shift_R)\
			and key.mask == KeyMask.ShiftMask + KeyMask.ReleaseMask:
				if self._prev_key.code == KeyCode.KEY_Shift_L or self._prev_key.code == KeyCode.KEY_Shift_R:
					self.change_mode()		
		self._prev_key = key
		if self._chinese_mode:
			return self.chinese_process_key_event (key)
		else:
			return self.english_process_key_event (key)

	def english_process_key_event (self, key):
		return False

	def chinese_process_key_event (self, key):
		if key.mask & KeyMask.ControlMask:
			if self._editor.is_empty ():
				return False
			if not (key.code >= KeyCode.KEY_0 and key.code <= KeyCode.KEY_9 or \
			(key.code in (KeyCode.KEY_c, KeyCode.KEY_b, KeyCode.KEY_f, KeyCode.KEY_e, KeyCode.KEY_h))):
				return False
		if self._editor.is_empty () and (key.code in (KeyCode.KEY_KP_Space, KeyCode.KEY_space,
			KeyCode.KEY_BackSpace, KeyCode.KEY_Delete,
			KeyCode.KEY_Return, KeyCode.KEY_KP_Enter,
			KeyCode.KEY_Escape, KeyCode.KEY_Up, KeyCode.KEY_Down,
			KeyCode.KEY_Page_Down, KeyCode.KEY_Page_Up, KeyCode.KEY_Home, KeyCode.KEY_End,
			KeyCode.KEY_Left,KeyCode.KEY_Right)\
			or key.code >= KeyCode.KEY_1 and key.code <= KeyCode.KEY_9):
			return False
		elif key.code in (KeyCode.KEY_KP_Space, KeyCode.KEY_space):
			if self._editor.pinyinlist:
				self._editor.convert_all ()
				return True
			elif self._editor.cursor < len (self._editor.wordlist):
				self._editor.jump_to_next_word()
				return True
			else:
				self.commit_string (self._editor.commit())
				return True
		elif key.code == KeyCode.KEY_BackSpace:
			self._editor.del_current ()
			return True
		elif key.code == KeyCode.KEY_Delete:
			self._editor.del_next ()
			return True
		elif key.code >= KeyCode.KEY_0 and key.code <= KeyCode.KEY_9 and key.mask & KeyMask.ControlMask:
			self._editor.move_cursor_to (key.code-KeyCode.KEY_0)
			return True
		elif key.code >= KeyCode.KEY_1 and key.code <= KeyCode.KEY_9:
			self._editor.select (key.code-KeyCode.KEY_1)
			return True
		elif not self._editor.is_end() and key.code in (KeyCode.KEY_Down, KeyCode.KEY_equal, KeyCode.KEY_bracketright, KeyCode.KEY_Page_Down):
			self._editor.turn_page (9)
			return True
		elif not self._editor.is_end() and key.code in (KeyCode.KEY_Up, KeyCode.KEY_minus, KeyCode.KEY_bracketleft, KeyCode.KEY_Page_Up):
			self._editor.turn_page (-9)
			return True		
		elif key.code == KeyCode.KEY_Left or key.code == KeyCode.KEY_b and key.mask & KeyMask.ControlMask:
			self._editor.move_cursor (-1)
			return True
		elif key.code == KeyCode.KEY_Right or key.code == KeyCode.KEY_f and key.mask & KeyMask.ControlMask:
			self._editor.move_cursor (1)
			return True
		elif key.code == KeyCode.KEY_h and key.mask & KeyMask.ControlMask or key.code == KeyCode.KEY_Home:
			self._editor.move_cursor_to (1)
			return True
		elif key.code == KeyCode.KEY_e and key.mask & KeyMask.ControlMask or key.code == KeyCode.KEY_End:
			self._editor.move_cursor_to (0)
			return True
		elif key.code in (KeyCode.KEY_Return, KeyCode.KEY_KP_Enter):
			self.commit_string (self._editor.commit())
			return True
		elif key.code == KeyCode.KEY_Escape or key.code == KeyCode.KEY_c and key.mask & KeyMask.ControlMask:
			self._editor.clear()
			return True
		elif key.code <= 127 and ascii.ispunct (chr (key.code)):
			if not self._editor.is_empty ():
				self.commit_string (self._editor.commit ())
			c = chr (key.code)
			self.commit_string (self.convert_to_full_width (unichr (key.code)))
			return True
		return False
	def convert_to_full_width (self, c):
		if c == u".":
			return u"\u3002"
		elif c == u"\\":
			return u"\u3001"
		elif c == u"^":
			return u"\u2026\u2026"
		elif c == u"_":
			return u"\u2014\u2014"
		elif c == u"$":
			return u"\uffe5"
		elif c == u"\"":
			self._double_quotation_state = not self._double_quotation_state
			if self._double_quotation_state:
				return u"\u201c"
			else:
				return u"\u201d"
		elif c == u"'":
			self._single_quotation_state = not self._single_quotation_state
			if self._single_quotation_state:
				return u"\u2018"
			else:
				return u"\u2019"
 		elif c == u"<":
 			return u"\u300a"
 		elif c == u">":
 			return u"\u300b"
		return scim.unichar_half_to_full (c)
		
class ShuangPinEngine (Engine):
	def __init__ (self, factory, config, encoding, id):
		Engine.__init__(self, factory, config, encoding, id)
	def chinese_process_key_event (self, key):
		try:
			if key.mask & KeyMask.ReleaseMask:
				return False
			if not key.mask & KeyMask.ControlMask and (\
				(key.code >= KeyCode.KEY_a and key.code <= KeyCode.KEY_z) or \
				(key.code >= KeyCode.KEY_A and key.code <= KeyCode.KEY_Z) or \
				key.code == KeyCode.KEY_semicolon):
				if(self._editor.current ()==None or self._editor.current ().is_complete ()):
					self._editor.insert (PinYinWord(SHUANGPIN_SHENGMU_DICT[unichr (key.code)],""))
				else:
					yunmu = SHUANGPIN_YUNMU_DICT[unichr(key.code)]
					p = None
					for i in yunmu:
						pinyin = self._editor.current ().get_shengmu () + i
						if pinyin in PINYIN_LIST:
							p = i
							break
					if p == None:
						raise Exception ()
					self._editor.current().set_yunmu (p)
					self._editor.update ()
					self._editor.auto_convert ()
				return True
			elif Engine.chinese_process_key_event (self,key):
				return True;
			return False
		except Exception, e:					
			self.beep ()
			p =open(os.path.expanduser ("~/.scim/scim-python.log"),'wb')
			traceback.print_exc (file=p)
			p.close ()
			return True
		finally:
			self.update()
			
class Factory (IMEngineFactory):
	def __init__ (self, config):
		IMEngineFactory.__init__ (self, config)
		self.name 		= _(u"ShuangPin")
		self.uuid 		= "7fb43ae9-0d72-4d7a-b255-f437ce28d510"
		self.authors	= u"Yu Fan <yufanyufan@gmail.com>"
		self.icon_file 	= "/usr/share/scim/icons/scim-python.png"
		self.credits 	= u"GPL"
		self.help		= _(u"Help For ShuangPin")
		self.set_languages ("zh")
		self._config	= config
	def create_instance (self, encoding, id):
		engine =  ShuangPinEngine (self, self._config, encoding, id)
		return engine

	def reload_config (self, config):
		pass


def test_add_char(editor, char):
	if(editor.current ()==None or editor.current ().is_complete ()):
		editor.insert(PinYinWord(SHUANGPIN_SHENGMU_DICT[char],""))
	else:
		yunmu = SHUANGPIN_YUNMU_DICT[char]
		p = None
		for i in yunmu:
			pinyin = editor.current ().get_shengmu() + i
			if pinyin in PINYIN_LIST:
				p = i
				break
		if p == None:
			raise Exception ()
		editor.current ().set_yunmu (p)
		editor.update ()
		editor.auto_convert ()
def test_case(pinyin,modify = None, sentence=None):
	editor = Editor ()
	for i in pinyin:
		test_add_char (editor,i)
	editor.convert_all ()
	if modify:
		for i in range (0,len(modify)):
			editor.wordlist[i].char=modify[i]
	result = editor.commit ()
	print result
	if sentence:
		assert editor.commit () == sentence

def test():
	test_case("fhdiijklfauhdedstmjqykllle")
	test_case("woxihryufj")
	test_case("vegeuurufahfhkys")

if __name__ == "__main__":
	import timeit
	t = timeit.Timer("ShuangPin.test()","import ShuangPin")
	print t.repeat(2,3)
	

